
import AxisLinePrimitive from './AxisLinePrimitive.js'
import TransformAxis from './TransformAxis.js'

let widgetOriginScratch$2 = new Cesium.Cartesian3()
let noScale = new Cesium.Cartesian3(1.0, 1.0, 1.0);
let matrixScratch = new Cesium.Matrix4();
let scaleScratch = new Cesium.Cartesian3()

let originScratch$1 = new Cesium.Cartesian3()
let directionScratch$1 = new Cesium.Cartesian3()
let planeNormalScratch$1 = new Cesium.Cartesian3()
let pickedPointScratch$1 = new Cesium.Cartesian3()
let moveScratch$1 = new Cesium.Cartesian3()
let offsetProjectedScratch = new Cesium.Cartesian3()
let rayScratch$2 = new Cesium.Ray()

export default class TranslationEditor {

  constructor(options = {}) {

    // 获取viewer.scene
    let scene = options.scene
    // 原点偏移坐标
    this.originOffset = options.originOffset || Cesium.Cartesian3.ZERO

    // 3条轴线  
    this._polylineX = scene.primitives.add(getLinePrimitive(TransformAxis.X))
    this._polylineY = scene.primitives.add(getLinePrimitive(TransformAxis.Y))
    this._polylineZ = scene.primitives.add(getLinePrimitive(TransformAxis.Z))

    this._scene = scene
    this._canvas = scene.canvas

    this._callback = options.callback
    this._modelMatrix = new Cesium.Matrix4()
    this._fixedFrame = new Cesium.Matrix4()
    this._hpr = new Cesium.HeadingPitchRoll()

    //拖动轴
    this._dragAlongVector = undefined
    // 偏移向量
    this._offsetVector = new Cesium.Cartesian3()
    // 创建平面
    this._pickingPlane = new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0)

    this._dragging = false
    this._active = false

    this._transform = options.transform
    this._radius = options.radius

    // 鼠标事件
    this._handler = new Cesium.ScreenSpaceEventHandler(this._scene.canvas)

    this.update()
  }


  // 更新事件
  bindEvent() {
    this._handler.setInputAction(this.handleLeftDown.bind(this ), Cesium.ScreenSpaceEventType.LEFT_DOWN)
    this._handler.setInputAction(this.handleLeftUp.bind(this), Cesium.ScreenSpaceEventType.LEFT_UP)
    this._handler.setInputAction(this.handleMouseMove.bind(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
  }

  /**
   *  解绑事件
   */
   unbindEvent(){
    this._handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN)
    this._handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)
    this._handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
  }

  update() {

    let transform = this._transform
    let ellipsoid = this._scene.mapProjection.ellipsoid


    let modelOrigin = Cesium.Matrix4.getTranslation(transform, originScratch$1)

    let widgetOrigin = getWidgetOrigin(transform, this.originOffset, widgetOriginScratch$2)

    let length = this._radius * Cesium.Matrix4.getMaximumScale(this._transform) * 1.5
    let hpr = Cesium.Transforms.fixedFrameToHeadingPitchRoll(this._transform, ellipsoid, undefined, this._hpr)
    hpr.pitch = 0
    hpr.roll = 0

    let hprToFF = Cesium.Transforms.headingPitchRollToFixedFrame(modelOrigin, hpr, ellipsoid, undefined, this._fixedFrame)
    // hprToFF = Cesium.Matrix4.setTranslation(hprToFF, widgetOrigin, hprToFF)
    let modelMatrix = Cesium.Matrix4.multiplyByUniformScale(hprToFF, length, this._modelMatrix)

    this._polylineX.modelMatrix = modelMatrix
    this._polylineY.modelMatrix = modelMatrix
    this._polylineZ.modelMatrix = modelMatrix
  }

  /**
   * 左键单击事件
   */
  handleLeftDown(position) {
    let scene = this._scene
    let camera = scene.camera
    position = position.position
    let pickedObjects = scene.drillPick(position)

    let pickedAxis;
    for (let i = 0; i < pickedObjects.length; i++) {
      let object = pickedObjects[i];
      if (Cesium.defined(object.id) && Cesium.defined(TransformAxis[object.id])) {
        pickedAxis = object.id
        break
      }
    }
    if (!Cesium.defined(pickedAxis)) {
      return
    }

    let origin = Cesium.Matrix4.getTranslation(this._transform, originScratch$1);
    let dragAlongVector = TransformAxis.getValue(pickedAxis);
    let directionVector = Cesium.Matrix4.multiplyByPointAsVector(this._fixedFrame, dragAlongVector, directionScratch$1);

    //Finds a picking plane that includes the dragged axis and is somewhat perpendicular to the camera
    let planeNormal = planeNormalScratch$1;
    if (Math.abs(Cesium.Cartesian3.dot(camera.upWC, directionVector)) > 0.7) {
      // if up and the direction are close to parellel, the dot product will be close to 1
      planeNormal = Cesium.Cartesian3.cross(camera.rightWC, directionVector, planeNormal);
    } else {
      planeNormal = Cesium.Cartesian3.cross(camera.upWC, directionVector, planeNormal);
    }
    Cesium.Cartesian3.normalize(planeNormal, planeNormal);

    let pickingPlane = Cesium.Plane.fromPointNormal(origin, planeNormal, this._pickingPlane);
    let offsetVector = Cesium.IntersectionTests.rayPlane(camera.getPickRay(position, rayScratch$2), pickingPlane, this._offsetVector);
    if (!Cesium.defined(offsetVector)) return
    Cesium.Cartesian3.subtract(offsetVector, origin, offsetVector);
    this._dragging = true;
    this._dragAlongVector = dragAlongVector;
    scene.screenSpaceCameraController.enableInputs = false;
  }

  /**
   * 鼠标移动
   * @param {*} position 
   * @returns 
   */
  handleMouseMove(position) {
    if (!this._dragging) return
    position = position.endPosition
    let scene = this._scene
    let camera = scene.camera

    // 获取射线与平面的焦点
    let pickedPoint = Cesium.IntersectionTests.rayPlane(camera.getPickRay(position, rayScratch$2), this._pickingPlane, pickedPointScratch$1);
    // 未发现焦点 返回
    if (!Cesium.defined(pickedPoint)) return

    // 获取抓取点的向量
    let dragAlongVector = this._dragAlongVector
    // 获取原始矩阵 将其偏移量保存至originScratch
    let origin = Cesium.Matrix4.getTranslation(this._transform, originScratch$1)
    let directionVector = Cesium.Matrix4.multiplyByPointAsVector(this._fixedFrame, dragAlongVector, directionScratch$1)
    // 计算平移点和原始点的差 算出平移的距离
    let moveVector = Cesium.Cartesian3.subtract(pickedPoint, origin, moveScratch$1)
    moveVector = Cesium.Cartesian3.projectVector(moveVector, directionVector, moveVector)
    let offset = Cesium.Cartesian3.projectVector(this._offsetVector, directionVector, offsetProjectedScratch)
    moveVector = Cesium.Cartesian3.subtract(moveVector, offset, moveVector)
    origin = Cesium.Cartesian3.add(origin, moveVector, origin)

    // 更新变换矩阵
    let translationMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin)
    // 获取角度
    let modelOriginRotation = Cesium.Transforms.fixedFrameToHeadingPitchRoll(this._transform)
    // 获取模型缩放
    let modelOriginScale = Cesium.Matrix4.getScale(this._transform, new Cesium.Cartesian3())
    // 根据模型原始角度计算矩阵
    let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(
      Cesium.Matrix3.fromHeadingPitchRoll(
        modelOriginRotation
      )
    )
    let scaleMatrix = Cesium.Matrix4.fromScale(modelOriginScale)
    let modelMatrix = new Cesium.Matrix4()
    // 平移旋转矩阵相乘
    Cesium.Matrix4.multiply(translationMatrix, rotationMatrix, translationMatrix);
    // 旋转缩放矩阵相乘
    Cesium.Matrix4.multiply(translationMatrix, scaleMatrix, modelMatrix)

    // 结果赋值
    this._transform = modelMatrix
    this.update()
    this._callback(modelMatrix)
  }

  /**
   * 鼠标抬起
   * @returns 
   */
  handleLeftUp() {
    if (!this._dragging) return
    this._dragging = false
    this._scene.screenSpaceCameraController.enableInputs = true;
  }


  isDestroyed() {
    return false
  }

  destroy() {
    this.active = false
    var scene = this._scene
    scene.primitives.remove(this._polylineX)
    scene.primitives.remove(this._polylineY)
    scene.primitives.remove(this._polylineZ)
    Cesium.destroyObject(this)
  }

  get callback(){
    return this._callback
  }

  set callback(val){
    this._callback = val
  }

  get radius(){
    return this._radius
  }

  set radius(val){
    this._radius = val
    this.update()
  }

  get transform (){
    return this._transform
  }

  set transform(val){
    this._transform  = val
    this.update()
  }

  get active() {
    return this._active
  }

  set active(active) {
    this._active = active

    if (active) {
      this.bindEvent()
      this._polylineX.show = true
      this._polylineY.show = true
      this._polylineZ.show = true
    } else {
      this.unbindEvent()
      this._polylineX.show = false
      this._polylineY.show = false
      this._polylineZ.show = false
      this._dragging = false
    }
  }

}

function getLinePrimitive(axis) {
  return new AxisLinePrimitive({
    positions: [Cesium.Cartesian3.ZERO, TransformAxis.getValue(axis)],
    arrow: true,
    width: 20,
    color: TransformAxis.getColor(axis),
    id: axis,
    show: false
  })
}


function getWidgetOrigin(transform, originOffset, result) {


  var startScale = Cesium.Matrix4.getScale(transform, scaleScratch)
  var modelMatrix = Cesium.Matrix4.setScale(transform, noScale, matrixScratch)

  return Cesium.Matrix4.multiplyByPoint(modelMatrix, Cesium.Cartesian3.multiplyComponents(originOffset, startScale, result), result);
}